package gate

import (
	"encoding/base64"
	"fmt"
	"go_learn/actor"
	"go_learn/base"
	"go_learn/mlog"
	"go_learn/network"
	"net"
	"reflect"
	"regexp"
	"strconv"
	"strings"
)

type UserData struct {
	version int
	hmac    []byte
	secret  []byte
	index   int
	fd      uint
	uid     uint64
	subid   int
}

func decodeUserName(username string) (uid uint64, subid int, server string, err error) {
	req := regexp.MustCompile(`([^@]+)@([^#]+)#([\s\S]*)`)
	params := req.FindStringSubmatch(username)
	if len(params) != 4 {
		err = fmt.Errorf("%s wrong format", username)
		return
	}
	var rst []byte
	rst, err = base64.StdEncoding.DecodeString(params[1])
	if err != nil {
		return
	}
	uid, err = strconv.ParseUint(string(rst), 10, 64)
	if err != nil {
		return
	}
	rst, err = base64.StdEncoding.DecodeString(params[2])
	if err != nil {
		return
	}
	subid, err = strconv.Atoi(string(rst))
	if err != nil {
		return
	}
	rst, err = base64.StdEncoding.DecodeString(params[3])
	if err != nil {
		return
	}
	server = string(rst)
	return
}

type agent struct {
	conn     *network.TCPConn
	gate     *Gate
	fd       uint
	bindAddr uint
	//登录验证相关信息
	userdata *UserData
}

func (a *agent) auth(fd uint, data []byte) error {
	splitRst := strings.Split(base.Bytes2Str(data), ":")
	username := splitRst[0]
	uid, subid, _, err := decodeUserName(username)
	if err != nil {
		return err
	}
	if _, ok := a.gate.onlineUsers[uid]; !ok {
		return nil
	}
	index, err := strconv.Atoi(splitRst[1])
	if err != nil {
		return err
	}
	hmac, err := base64.StdEncoding.DecodeString(splitRst[2])
	if err != nil {
		return err
	}
	u, ok := a.gate.onlineUsers[uid]
	if !ok {
		return fmt.Errorf("can't find user %d", uid)
	}
	if index <= u.version {
		return fmt.Errorf("user[%d] Index Expired", uid)
	}
	if !base.ByteSliceEqualBCE(u.secret, hmac) {
		return fmt.Errorf("auth user[%d] secret fail", uid)
	}
	u.fd = fd
	u.version = index
	u.hmac = hmac
	u.subid = subid
	u.uid = uid
	a.userdata = u
	delete(a.gate.unauthAgents, fd)
	return nil
}

func (a *agent) request(data []byte) error {
	if a.gate.Processor != nil {
		msg, mi, err := a.gate.Processor.Unmarshal(data)
		if err != nil {
			mlog.Debugf("unmarshal message error: %v", err)
			return err
		}
		if a.bindAddr != 0 {
			a.gate.Send(a.bindAddr, actor.Client, mi.Method, []interface{}{mi.Package, msg})
		} else {
			a.gate.Send(mi.Package, actor.Client, mi.Method, []interface{}{msg})
		}
	}
	return nil
}

func (a *agent) Run() {
	for {
		data, err := a.conn.ReadPackge()
		if err != nil {
			mlog.Debugf("read message: %v", err)
			break
		}
		if _, ok := a.gate.unauthAgents[a.fd]; ok {
			err = a.auth(a.fd, data)
		} else {
			err = a.request(data)
		}
		if err != nil {
			mlog.Debugf("read message: %v", err)
			break
		}
	}
}

func (a *agent) OnClose() {
	if a.bindAddr != 0 {
		a.gate.Send(a.bindAddr, actor.Socket, "OnClose", []interface{}{a.fd})
	}
	a.gate.OnDisconnect(a.fd)
}

func (a *agent) WriteMsg(msg interface{}) {
	if a.gate.Processor != nil {
		data, err := a.gate.Processor.Marshal(msg)
		if err != nil {
			mlog.Errorf("marshal message %v error: %v", reflect.TypeOf(msg), err)
			return
		}
		err = a.conn.AsyncWrite(data)
		if err != nil {
			mlog.Errorf("write message %v error: %v", reflect.TypeOf(msg), err)
		}
	}
}

func (a *agent) LocalAddr() net.Addr {
	return a.conn.LocalAddr()
}

func (a *agent) RemoteAddr() net.Addr {
	return a.conn.RemoteAddr()
}

func (a *agent) Close() {
	a.conn.Close()
}

func (a *agent) Destroy() {
	a.conn.Destroy()
}
